package com.fingard.net.listener;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.fingard.FGBiz;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.directConfig.ListenerSet;
import com.fingard.net.IpHelper;
import com.fingard.text.StringHelper;

import javax.net.ssl.*;

public class HttpListener implements Runnable {

    private int listenPort = 0;
    private ServerSocket socketServer;
    private Socket tmpSocket;
    private SSLServerSocket sslServerSocket;
    private String initCharset;
    private String protocol;//协议 https/http/socket
    private String keyStore;//密钥库keystore jsk
    private String keyStorePwd;//密钥库访问密码
    private String encryProtocol;//加密协议SSL、TLS
    //private String bankCode;
    //public BankFront bankFront = null;
    private ExecutorService esThreadPool;

    /**
     * p_listenPort,p_charset,p_bankCode
     */
    public HttpListener(ListenerSet p_set) {
        listenPort = p_set.listenerPort;
        initCharset = p_set.charset;
        protocol=p_set.protocol;
        keyStore=p_set.keyStore;
        keyStorePwd=p_set.keyStorePwd;
        encryProtocol=p_set.encryProtocol;
        addListenerSet(p_set);
        //charset = p_set.charset;
        //bankCode = p_bankCode;
        //if(StringHelper.isNullOrEmpty(charset)){
        //	charset = java.nio.charset.Charset.defaultCharset().name();
        //}
        esThreadPool = Executors.newCachedThreadPool();
    }

    private HashMap<String, ListenerSet> mapForBankClass = null;

    /**
     * SSL协议
     * @return
     */
    private SSLServerSocket sslSocketCheck(HttpListener httpListener){
        SSLServerSocket server=null;
        try {
            String keyName = httpListener.keyStore;//密钥库
            char[] keyPwd = httpListener.keyStorePwd.toCharArray();//密钥库密码
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            // 装载当前目录下的keystore
            InputStream keyFile = new FileInputStream(keyName);
            keyStore.load(keyFile , keyPwd);
            keyFile.close();
            // 初始化key
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(keyStore, keyPwd);
            // 初始化ssl
            SSLContext context = SSLContext.getInstance(httpListener.encryProtocol);
            context.init(kmf.getKeyManagers(), new TrustManager[] { new MyX509TrustManager()}, new SecureRandom());
            // 获取SSLContext实例
            //SSLEngine e = context.createSSLEngine();
            //FGBiz.startLog.addLogNoTime("支持的协议: " + Arrays.asList(e.getSupportedProtocols()));
            //FGBiz.startLog.addLogNoTime("启用的协议: " + Arrays.asList(e.getEnabledProtocols()));
            //FGBiz.startLog.addLogNoTime("支持的加密套件: " + Arrays.asList(e.getSupportedCipherSuites()));
            //FGBiz.startLog.addLogNoTime("启用的加密套件: " + Arrays.asList(e.getEnabledCipherSuites()));
            // 监听和接收客户端连接
            SSLServerSocketFactory factory = context.getServerSocketFactory();
            server = (SSLServerSocket) factory.createServerSocket(httpListener.listenPort);
        } catch (KeyStoreException e) {
            e.printStackTrace();
            FGBiz.logTrace.exception("sslSocketCheck", e);
        } catch (IOException e) {
            e.printStackTrace();
            FGBiz.logTrace.exception("sslSocketCheck", e);
        } catch (CertificateException e) {
            e.printStackTrace();
            FGBiz.logTrace.exception("sslSocketCheck", e);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            FGBiz.logTrace.exception("sslSocketCheck", e);
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
            FGBiz.logTrace.exception("sslSocketCheck", e);
        } catch (KeyManagementException e) {
            e.printStackTrace();
            FGBiz.logTrace.exception("sslSocketCheck", e);
        }
        return server;
    }

    public void addListenerSet(ListenerSet p_set) {
        if (mapForBankClass == null) {
            mapForBankClass = new HashMap<String, ListenerSet>();
        }

        String visitPage = StringHelper.trimEnd(p_set.prefix.toLowerCase(), "/");
        if (StringHelper.isNullOrEmpty(visitPage)) {
            visitPage = "/";
        }
        if (mapForBankClass.containsKey(visitPage)) {
        	FGBiz.limitMsg.error("HttpListener.addListenerSet", "已存在侦听地址" + p_set.prefix + "，对应BankCode为" + mapForBankClass.get(visitPage).bankFront.bankCode);
        } else {
            mapForBankClass.put(visitPage, p_set);
        }
    }

    public void run() {
        start();
    }

    public void start() {

        try {
            if("https".equals(protocol.toLowerCase())){
                sslServerSocket=sslSocketCheck(this);
            }else {
                socketServer = new ServerSocket(listenPort, 20);
            }
            int tmpSeqID = 0;
            while (true) {
                if("https".equals(protocol.toLowerCase())){
                    if(sslServerSocket!=null){
                        tmpSocket=sslServerSocket.accept();
                    }else {
                        FGBiz.limitMsg.error("HttpListener.start", "https监听启动异常，请检查密钥库或前置机配置");
                        return;
                    }
                }else {
                    tmpSocket = socketServer.accept();
                }
                tmpSeqID++;
                
                if("http".equals(protocol.toLowerCase()) || "https".equals(protocol.toLowerCase())) {
                	HttpReceiver reqHttpReceiver = new HttpReceiver(tmpSocket, initCharset);
                    reqHttpReceiver.port = listenPort;
                    reqHttpReceiver.ip = IpHelper.getLocalHostIp();
                    reqHttpReceiver.connTime = new Date();
                    reqHttpReceiver.seqIDInner = tmpSeqID;
                    //开一个线程去处理
                    HttpThread tmpEachThd = new HttpThread(reqHttpReceiver, mapForBankClass);
                    //new Thread(tmpEachThd).start();
                    esThreadPool.execute(tmpEachThd);
                }else if("socket".equals(protocol.toLowerCase())){
                    SocketReceiver reqSocketReceiver=new SocketReceiver(tmpSocket, initCharset);
                    reqSocketReceiver.port = listenPort;
                    reqSocketReceiver.ip = IpHelper.getLocalHostIp();
                    reqSocketReceiver.connTime = new Date();
                    reqSocketReceiver.seqIDInner = tmpSeqID;
                    SocketThread tmpEachThd=new SocketThread(reqSocketReceiver, mapForBankClass);
                    //new Thread(tmpEachThd).start();
                    esThreadPool.execute(tmpEachThd);
                }
            }
        } catch (Exception e) {
        	FGBiz.limitMsg.exception("HttpListener.start", e);
        }
    }

    public String stop() {
    	String retStr = "";
        if (socketServer != null) {
            try {
                socketServer.close();
                retStr = "服务端口"+listenPort+"正常关闭";
            } catch (IOException e) {
            	retStr = "服务端口"+listenPort+"关闭出现异常："+LogHelper.getStackTrace(e);
                FGBiz.limitMsg.exception("HttpListener.stop", e);
            }
        }
        esThreadPool.shutdown();
        return retStr;
    }
}
